#include "redis.hpp"
#include "EventLoop.hpp"
#include "Logger.hpp"
#include <chrono>
#include <cstddef>
#include <cstdio>
#include <hiredis/async.h>
#include <hiredis/hiredis.h>
#include <iostream>

using namespace zephyr;
redisContext *begin() {
  redisContext *c = redisConnect("127.0.0.1", 6379);
  if (c == NULL || c->err) {
    if (c) {
      std::cout << "Error: " << c->errstr << std::endl;
      redisFree(c);
    } else {
      std::cout << "Can't allocate redis context" << std::endl;
    }
  }
  return c;
}

void end(redisContext *c) { redisFree(c); }

void reset(redisContext *c) {
  redisReply *reply = (redisReply *)redisCommand(c, "keys *");
  if (reply->type == REDIS_REPLY_ARRAY) {
    for (int i = 0; i < reply->elements; i++) {
      std::cout << "[" << i << "]: " << reply->element[i]->str << std::endl;
      redisReply *delReply =
          (redisReply *)redisCommand(c, "del %s", reply->element[i]->str);
      freeReplyObject(delReply);
    }
  }
}

void test() {
  redisContext *c;
  redisReply *reply;

  c = redisConnect("127.0.0.1", 6379);
  if (c == NULL || c->err) {
    if (c) {
      std::cout << "Error: " << c->errstr << std::endl;
      redisFree(c);
    } else {
      std::cout << "Can't allocate redis context" << std::endl;
    }
  }

  reply = (redisReply *)redisCommand(c, "ping");
  std::cout << reply->str << std::endl;
  freeReplyObject(reply);

  reply = (redisReply *)redisCommand(c, "set %s %s", "foo", "hello world");
  std::cout << "set: " << reply->str << std::endl;
  freeReplyObject(reply);

  reply = (redisReply *)redisCommand(c, "SET %b %b", "bar", (size_t)3, "hello",
                                     (size_t)5);
  printf("SET (binary API): %s\n", reply->str);
  freeReplyObject(reply);

  reply = (redisReply *)redisCommand(c, "get foo");
  std::cout << "get foo: " << reply->str << std::endl;
  freeReplyObject(reply);

  reply = (redisReply *)redisCommand(c, "INCR counter");
  printf("INCR counter: %lld\n", reply->integer);
  freeReplyObject(reply);
  /* again ... */
  reply = (redisReply *)redisCommand(c, "INCR counter");
  printf("INCR counter: %lld\n", reply->integer);
  freeReplyObject(reply);

  /* Create a list of numbers, from 0 to 9 */
  reply = (redisReply *)redisCommand(c, "del mylist");
  freeReplyObject(reply);
  for (int i = 0; i < 10; i++) {
    char buf[64] = {0};
    snprintf(buf, 64, "%u", i);
    reply = (redisReply *)redisCommand(c, "lpush mylist element-%s", buf);
    freeReplyObject(reply);
  }

  /* Let's check what we have inside the list */
  reply = (redisReply *)redisCommand(c, "lrange mylist 0 -1");
  if (reply->type == REDIS_REPLY_ARRAY) {
    for (int i = 0; i < reply->elements; i++) {
      std::cout << "[" << i << "]: " << reply->element[i]->str << std::endl;
    }
  }
  freeReplyObject(reply);

  redisFree(c);
}

void syncTest() {
  auto c = begin();
  int num = 1000;
  redisReply *reply;
  auto before_tick = std::chrono::steady_clock::now();
  for (int i = 0; i < num; i++) {
    reply = (redisReply *)redisCommand(c, "INCR counter");
    // printf("INCR counter: %lld\n", reply->integer);
    freeReplyObject(reply);
  }
  auto use = std::chrono::duration_cast<std::chrono::microseconds>(
      std::chrono::steady_clock::now() - before_tick);
  std::cout << "after " << num << " exec redis command, used " << use.count()
            << "us" << std::endl;
  // reset(c);
  end(c);
}


using namespace std::chrono;
static int num, count = 0;
static time_point<steady_clock> before_tick;
void getCallback(redisAsyncContext *ac, void *r, void *privdata) {
  redisReply *reply = (redisReply *)r;
  if (reply == NULL)
    return;
  // printf("argv[%s]: %lld\n", (char *)privdata, reply->integer);

  ++count;
  if (count == num) {
    auto use = std::chrono::duration_cast<std::chrono::microseconds>(
        std::chrono::steady_clock::now() - before_tick);
    std::cout << "after " << num << " exec redis command, used " << use.count()
              << "us" << std::endl;
  }
}

void asyncClientOODTest() {
  zephyr::EventLoop loop;
  RedisAsyncClient rclient{&loop, "127.0.0.1", 6379};

  rclient.connect();
  num = 1000;
  before_tick = std::chrono::steady_clock::now();
  for (int i = 0; i < num; i++)
    rclient.command("incr counter", &getCallback, (void *)"count");

  loop.loop();
}

int main() {
  syncTest();
  asyncClientOODTest();
  return 0;
}